#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>


#include "common.h"
#include "clientFunction.h"


void *TaskCode(void *argument)
{
   job_t* job = (job_t*) argument;
   printf("start the job\n");
   job->res = clientFunction(job);
   printf("Finito\n");

   /* optionally: insert more useful stuff here */

   return NULL;
}

int main(int argc, char **argv)
{
	int sockfd;                   // descripteur de socket
	struct sockaddr_in serveur;   // structure d'adresse qui contiendra les param reseaux du serveur
	fd_set readfds;               // ensemble des descripteurs en lecture qui seront surveilles par select
	char buf[1024];               // espace necessaire pour stocker le message recu
	char sendBuffer[1024];              // espace necessaire pour envoyer un message au serveur
	int threadExist = 0;
	struct timeval timeout;
	job_t currentJob;
	
	memset(buf,'\0',1024);        // initialisation du buffer qui sera utilisé
	memset(sendBuffer,'\0',1024);       // initialisation de l'autre buffer qui sera utilisé


	pthread_t thread;
	int rc, socketFlags;

	// verification du nombre d'arguments sur la ligne de commande
	if(argc != 3)
	{
		printf("Usage: %s @serveur port_serveur\n", argv[0]);
		exit(-1);
	}

	// creation de la socket
	sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);


	socketFlags=fcntl(sockfd,F_GETFL);              // Get socket flags
	fcntl(sockfd,F_SETFL,socketFlags | O_NONBLOCK);   // Add non-blocking flagy

	// initialisation de la structure d'adresse du serveur :

	// famille d'adresse
	serveur.sin_family = AF_INET;

	// recuperation de l'adresse IPv4 du serveur
	inet_aton(argv[1], &(serveur.sin_addr));

	// recuperation du port du serveur
	serveur.sin_port = htons(atoi(argv[2]));

	printf("Tentative de connexion\n");

	// tentative de connexion
	/*if(connect(sockfd,(struct sockaddr*)&serveur,sizeof(serveur)) == -1 && errno() != EINPROGRESS)
	{
	perror("Erreur de connexion -> ");
	exit(2);
	}*/
	
	timeout.tv_sec  = 1;
	timeout.tv_usec = 0;

	connect(sockfd,(struct sockaddr*)&serveur,sizeof(serveur));

	printf("Connexion etablie\n");

	while(1)
	{
		//printf("A loop starts\n");
		memset(buf,'\0',1024); // on reinitialise les buffers qui seront utilises
		memset(sendBuffer,'\0',1024);

		FD_ZERO(&readfds); // il faut remettre tt les elements ds readfds a chaque recommencement de la boucle, vu que select modifie les ensembles
		//FD_SET(0,&readfds);                                       // on rajoute l'entree standard
		FD_SET(sockfd,&readfds);                                  // on rajoute la socket de communication avec le serveur

		if(select(sockfd+1,&readfds,NULL,NULL,&timeout) == -1) {
		   	perror("Erreur lors de l'appel a select -> ");
		   	exit(1);
		}

		/*if(FD_ISSET(0,&readfds)){     // si l'entree standard est dans readfds, alors l'utilisateur en en train de rédiger un message a envoyer
			printf("if_isset 1\n");
			if(read(0,sendBuffer,1024) == -1){                             // on lit donc ce qui arrive sur l'entrée standard
				perror("Erreur lors de l'appel a read -> ");
				exit(1);
			}
			if(send(sockfd,sendBuffer,1024,0) == -1){                      // puis on l'envoie au serveur
				perror("Erreur lors de l'appel a send -> ");
				exit(1);
			}
		}*/


		if(FD_ISSET(sockfd,&readfds) && threadExist == 0) {  // si la socket de communication est dans readfds, alors le serveur nous a envoye un message
			int msgSize = recv(sockfd, &buf, 1024, 0);
			if(msgSize == -1){
				perror("Erreur lors de la reception -> ");
				exit(4);
			}
			else if(msgSize == 0)
			{
				printf("server has terminated connexion\n");
				close(sockfd); // fermeture de la socket
				break;
			}
			else if (buf[0] == 'M')
			{
				memcpy(&currentJob, buf+1, sizeof(job_t));
				currentJob.state = RUNNING;
				rc = pthread_create(&thread, NULL, TaskCode, (void *) &currentJob);
				if (!rc) {
					threadExist = 1;
				}
			}
			else
			{
				buf[msgSize] = '\0'; // ajout du '\0' pour affichage
				printf("La chaine recue est: %s",buf);            // on l'affiche
			}
		}

		if (threadExist == 1 && !pthread_tryjoin_np(thread, (void **) NULL))
		{
			currentJob.state = DONE;
			sendBuffer[0] = 'D';
			memcpy(sendBuffer+1, &currentJob, sizeof(job_t)); 
			if(send(sockfd,sendBuffer,1024,0) == -1)  // tell the serv the job is done
			{                     
				perror("Erreur lors de l'appel a send -> ");
				//exit(1);
			}
			else
			{
				printf("Job done send %llu\n", currentJob.start);
			}
			threadExist = 0;
			
		}
		/* wait for all threads to complete */
		//printf("A loop ends\n");
	}

	return 1;
}


